package io.elegant.sessions.media

import android.content.Context
import android.media.MediaPlayer
import com.google.protobuf.Any
import io.elegant.*
import io.elegant.ElegantApiGrpc.ElegantApiStub
import io.elegant.sessions.BaseSession
import io.elegant.sessions.widgets.WidgetFileUploaderSession
import io.grpc.stub.StreamObserver
import kotlinx.coroutines.launch

class MediaPlayerSession(
    stub: ElegantApiStub,
    private val context: Context
) : BaseSession(stub, context) {
    override val sendStreamObserver: StreamObserver<Elegant.Interaction> = stub.mediaPlayer(receiveStreamObserver)
    private var player: MediaPlayer? = null
    private var errorCallbacks: MutableSet<(Int, Int) -> Unit>? = null
    private fun catchMediaPlayerError(sessionId: Long, futureId: Long) {
        fun cb(i: Int, i2: Int) {
            Exception("An error occurred in the media player, and the error codes are ($i, $i2).")
                .sendError(sessionId, futureId)
            errorCallbacks?.remove(::cb)
        }
        errorCallbacks?.add (::cb)
    }

    override fun onCreate(sessionId: Long, futureId: Long, vararg argv: Any) {
        super.onCreate(sessionId, futureId, *argv)
        errorCallbacks = mutableSetOf()
        player = MediaPlayer()
        player?.setOnErrorListener { _, i, i2 ->
            errorCallbacks?.forEach {
                it(i, i2)
            }
            true
        }
    }

    override fun onDestroy() {
        player?.release()
        player = null
        errorCallbacks?.clear()
        errorCallbacks = null
        super.onDestroy()
    }

    override suspend fun onReceive(name: String, sessionId: Long, futureId: Long, vararg argv: Any) {
        when (name) {
            // 调用模块方法
            "load" -> load(sessionId, futureId, *argv)
            "loadFromWidgetFileUploader" -> loadFromWidgetFileUploader(sessionId, futureId, *argv)
            "loadFromMediaRecorder" -> loadFromMediaRecorder(sessionId, futureId, *argv)
            "stop" -> stop(sessionId, futureId)
            "play" -> play()
            "pause" -> pause()

            // 获取属性
            "isPlaying" -> sendAction("onIsPlaying", sessionId, futureId, player?.isPlaying.toAny())
            "duration" -> sendAction("onDuration", sessionId, futureId, player?.duration.toAny())
            "currentPosition" -> sendAction("onCurrentPosition", sessionId, futureId, player?.currentPosition.toAny())
            "isLooping" -> sendAction("onIsLooping", sessionId, futureId, player?.isLooping.toAny())

            // 设置属性
            "setLooping" -> setLooping(*argv)
            "setSpeed" -> setSpeed(*argv)
            "setVolume" -> setVolume(*argv)
            "seekTo" -> seekTo(*argv)

            // 绑定回调方法
            "setOnCompletionListener" -> setOnCompletionListener(*argv)

            else -> super.onReceive(name, sessionId, futureId, *argv)
        }
    }

    private fun setOnCompletionListener(vararg argv: Any) = player?.setOnCompletionListener {
        csMain.launch {
            argv[0].sendCallback()
        }
    }

    private fun load(sessionId: Long, futureId: Long, vararg argv: Any) {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `url` from the server calling this `load` method.")
        catchMediaPlayerError(sessionId, futureId)
        val url = argv[0].toStringValue()
        player?.apply {
            reset()
            setDataSource(url)
            setOnPreparedListener {
                onLoad(argv[0], sessionId, futureId)
            }
            prepareAsync()
        } ?: throw IllegalStateException("The player have not been created from the server calling this `load` method.")
    }

    private fun loadFromWidgetFileUploader(sessionId: Long, futureId: Long, vararg argv: Any) {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `uploader` from the server calling this `loadFromWidgetFileUploader` method.")
        catchMediaPlayerError(sessionId, futureId)
        val uploader = argv[0].getReference<WidgetFileUploaderSession>()
        uploader?.setOnFileSelectedListener {
            player?.apply {
                reset()
                it?.let {
                    setDataSource(context, it)
                }
                prepareAsync()
            } ?: throw IllegalStateException("The player have not been created from the server calling this `loadFromWidgetFileUploader` method.")
        }
        onLoadFromWidgetFileUploader(true.toAny(), sessionId, futureId)
    }
    private fun loadFromMediaRecorder(sessionId: Long, futureId: Long, vararg argv: Any) {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `recorder` from the server calling this `loadFromMediaRecorder` method.")
        catchMediaPlayerError(sessionId, futureId)
        val recorder = argv[0].getReference<MediaRecorderSession>()
        recorder?.setOnRecorded {
            player?.apply {
                reset()
                it?.let {
                    setDataSource(it.path)
                }
                prepareAsync()
            } ?: throw IllegalStateException("The player have not been created from the server calling this `loadFromMediaRecorder` method.")
        }
        onLoadFromMediaRecorder(true.toAny(), sessionId, futureId)
    }
    private fun play() = player?.start()
    private fun stop(sessionId: Long, futureId: Long) = player?.let {
        it.stop()
        it.setOnPreparedListener {
            onStop(true.toAny(), sessionId, futureId)
        }
        try {
            it.prepareAsync()
        } catch (e: IllegalStateException) {
            e.printStackTrace()
            onStop(false.toAny(), sessionId, futureId)
        }
    }

    private fun pause() = player?.pause()
    private fun setLooping(vararg argv: Any) = player?.let {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `enabled` from the server calling this `setLooping` method.")
        it.isLooping = argv[0].toBoolValue()
    }

    private fun setSpeed(vararg argv: Any) = player?.apply {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `rate` from the server calling this `setSpeed` method.")
        playbackParams = playbackParams.also {
            it.speed = argv[0].toFloatValue()
        }
    }

    private fun setVolume(vararg argv: Any) = player?.let {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `volume` from the server calling this `setVolume` method.")
        val left = argv[0].toFloatValue()
        val right = if (argv.size > 1) argv[1].toFloatValue() else left
        it.setVolume(left, right)
    }

    private fun seekTo(vararg argv: Any) = player?.let {
        if (argv.isEmpty())
            throw IllegalArgumentException("Missing param `mse` from the server calling this `seekTo` method.")

        val mse = argv[0].toIntValue()
        if (mse > 0 && mse < it.duration) {
            it.seekTo(mse)
        }
    }

    private fun onLoad(url: Any, sessionId: Long, futureId: Long) = sendAction("onLoad", sessionId, futureId, url)
    private fun onLoadFromWidgetFileUploader(result: Any, sessionId: Long, futureId: Long) = sendAction("onLoadFromWidgetFileUploader", sessionId, futureId, result)
    private fun onLoadFromMediaRecorder(result: Any, sessionId: Long, futureId: Long) = sendAction("onLoadFromMediaRecorder", sessionId, futureId, result)
    private fun onStop(result: Any, sessionId: Long, futureId: Long) = sendAction("onStop", sessionId, futureId, result)

}